package com.stary.springmvc.web.dataValid;

import com.stary.springmvc.web.dataValid.entity.Monster;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.validation.Valid;
import java.util.List;
import java.util.Map;

/**
 * @author: Guan
 **/
@Controller
@Scope(value = "prototype") //表示每次请求MonsterHandler会生成一个新的对象
public class MonsterHandler {
    //显示添加monster的页面
    //注意一点，如果参数是Map<String, Object> map,当我们向map添加数据时，会默认存放到request域中
    @RequestMapping(value = "/addMonsterUI")
    public String addMonsterUI(Map<String, Object> map) {
    /**老韩解读:(不是重点)
     1. 这里的表单，我们使用 springMVC 的标签来完成
     2. SpringMVC 表单标签在显示之前必须在 request 中有一个 bean, 该 bean的属性和表单标签的字段要对应!
     request 中的 key 为: form 标签的 modelAttrite 属性值， 比如这里的 monsters
     3. SpringMVC 的 form:form 标签的 action 属性值中的 / 不代表 WEB 应用的根目录.
     4. <form:form action="monster" method="POST" modelAttribute="monster">
     // 这 里 需 要 给 request 增 加 一 个 monster ， 因 为 jsp 页 面 的modelAttribute="monster"需要
     //这时是 springMVC 的内部的检测机制 即使是一个空的也需要，否则报错.
     */
        //老韩再次说明，如果你跳转的页面使用springmvc标签
        //就需要准备一个对象，放入request域中，这个对象的属性名 monster, 对于
        //springmvc表单标签的 modelAttribute="monster"
        map.put("monster", new Monster());
        //<property name="prefix" value="/WEB-INF/pages/"></property>,后缀也补充完了
        return "datavalid/monster_addUI";
    }

    /**
     * 编写方法，处理添加妖怪
     * 补充：springmvc中可以将提交的数据，按照参数名的方式和对象的属性名匹配
     * age属性由前端的String转换成了Integer
     * @param monster
     * @return
     */
    @RequestMapping(value = "/save")
    public String save(Monster monster){
        System.out.println("--monster--" + monster);
        return "datavalid/success";
    }

    /**
     * 数据校验
     * 校验发生时机：在springmvc底层，反射调用方法的时候，会接受到http请求的数据，然后根据注解来进行验证
     * 在验证过程中，如果出现错误，就把错误信息填充到errors和map中
     * @Valid 表示对montser接收的进行校验
     * @param monster
     * @param errors 如果校验出现错误，则将校验的错误信息保存到 error中
     * @param map 如果校验出现错误，校验的错误信息也会保存到map中，同时保存monster对象
     * @return
     */
    @RequestMapping(value = "/save2")
    public String save2(@Valid Monster monster, Errors errors, Map<String,Object> map){
        System.out.println("--monster--" + monster);

        System.out.println("=============map.entry=============");
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
            System.out.println("entry = " + entry);
        }


        System.out.println("==========================");
        List<ObjectError> allErrors = errors.getAllErrors();
        if (errors.hasErrors()){  //判断是否有错误
            for (ObjectError allError : allErrors) {
                System.out.println("allError = " + allError);
            }
            return "datavalid/monster_addUI";
        }

        return "datavalid/success";
    }

    /**
     * 取消绑定 monster的name表单提交的值给monster.name属性
     * 解读：
     *  1.方法上需要标注@InitBinder注解
     *  2.SpringMVC会初始化WebDataBinder对象
     *  3.调用setDisallowedFields方法，参数为字段名称，取消制定属性的绑定
     *     即当表单提交的字段名为name值就不在把接收到的name值填充到Model数据monster的name属性
     *
     * 机制：
     *  springMVC在底层通过反射调用目标方法时，会接收到Http请求的参数和值，会使用反射+注解技术取消对制定属性的填充
     *
     * 补充：
     *  1.setDisallowedFields支持可变参数，可以添加多个字段  webDataBinder.setDisallowedFields("name","email");
     *  2.如果我么取消了某个属性绑定，那么验证也就没了意义，应当把验证的注解去掉，否则可能报错。
     *    例如我们为字段修饰了一个@NotNull注解，则会报错
     *  3.字段会使用他的默认值null。private String name = "XXX";则nam值为"XXX"
     *  4.段会使用他的默认值null，但他输出结果可能为'null'，无需在意，类型就是null，可以使用debug进行验证
     *
     * @param webDataBinder
     */

    @InitBinder
    public void initBinder(WebDataBinder webDataBinder){
        // webDataBinder.setDisallowedFields("name");
    }
}
